/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		win32expt.cpp
 * PURPOSE:		SEH exception usage implementation and common support
 *				
 */

//already defined in ntstatus.h
//const unsigned STATUS_SEVERITY_SUCCESS =        0x0;
//const unsigned STATUS_SEVERITY_INFORMATIONAL =  0x1;
//const unsigned STATUS_SEVERITY_WARNING =        0x2;
//const unsigned STATUS_SEVERITY_ERROR =          0x3;

const unsigned STATUS_SEVERITY_LSHIFT =			30;
const unsigned STATUS_FACILITY_LSHIFT =			16;

#define NTSTATUS_VALUE(ExtraFlags, Severity, Facility, Number) ((ExtraFlags) | (Severity) << STATUS_SEVERITY_LSHIFT | ((Facility) << STATUS_FACILITY_LSHIFT) | (Number))

const unsigned STATUS_CONSUMER =				0x20000000;

const unsigned FACILITY_SDEDIT =				0x22;

const unsigned STATUS_NT_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x01); //user-error #1
const unsigned STATUS_WIN32_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x02);
const unsigned STATUS_STDC_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x03);
const unsigned STATUS_ROW_DELETED =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x04);
const unsigned STATUS_CANCEL_ONE =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x05);
const unsigned STATUS_CANCEL_ALL =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x06);
const unsigned STATUS_ADSI_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x07);
const unsigned STATUS_LDAP_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x08);
const unsigned STATUS_TEXT_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x09);
const unsigned STATUS_COM_ERROR =				NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_ERROR, FACILITY_SDEDIT, 0x10);

const unsigned STATUS_COUNT_CANCEL_HANDLERS =	NTSTATUS_VALUE(STATUS_CONSUMER, STATUS_SEVERITY_INFORMATIONAL, FACILITY_SDEDIT, 0x02);

#undef FACILITY_VISUALCPP
const unsigned FACILITY_VISUALCPP =				0x6d;

const unsigned STATUS_VCPP_MOD_NOT_FOUND =		NTSTATUS_VALUE(0, STATUS_SEVERITY_ERROR, FACILITY_VISUALCPP, ERROR_MOD_NOT_FOUND);
const unsigned STATUS_VCPP_PROC_NOT_FOUND =		NTSTATUS_VALUE(0, STATUS_SEVERITY_ERROR, FACILITY_VISUALCPP, ERROR_PROC_NOT_FOUND);

ULONG OpTextStackTls = 0;
ULONG CppCancelHandlerCountTls = 0;

void InitExptHelp()
{
	OpTextStackTls = TlsAlloc();
	CppCancelHandlerCountTls = TlsAlloc();
}

void FreeExptHelp()
{
	TlsFree(OpTextStackTls);
	TlsFree(CppCancelHandlerCountTls);
}

struct OpTextThread
{
	OpTextThread()
	{
		TlsSetValue(OpTextStackTls, 0);
		TlsSetValue(CppCancelHandlerCountTls, 0);
	}
};

//all per-thread resources used by a SD Edit thread-- lifetime tied to executing SD Edit functions on a thread
struct SdEditThreadResources
{
	ThreadSynchEvent Tse;
	OpTextThread Ott;
};

struct OpText
{
	OpText *Up;
	PCWSTR Operation;
	PCWSTR Object;

	OpText(PCWSTR Operation = NULL, PCWSTR Object = NULL) :
		Up(NULL),
		Operation(Operation),
		Object(Object)
	{
		Up = c_cast<OpText *>(TlsGetValue(OpTextStackTls));
		TlsSetValue(OpTextStackTls, this);
	}
	void SetText(PCWSTR Operation = NULL, PCWSTR Object = NULL)
	{
		this->Operation = Operation;
		this->Object = Object;
	}
	~OpText()
	{
		OpText *PrevOt = c_cast<OpText *>(TlsGetValue(OpTextStackTls));

		if(this != PrevOt)
			RaiseException(static_cast<DWORD>(STATUS_INTERNAL_ERROR), EXCEPTION_NONCONTINUABLE, 0, NULL);

		TlsSetValue(OpTextStackTls, Up);
	}
};

BOOLEAN IsExpectedException(ULONG Code)
{
	switch(Code)
	{
		case STATUS_NT_ERROR:
		case STATUS_WIN32_ERROR:
		case STATUS_STDC_ERROR:
		case STATUS_ADSI_ERROR:
		case STATUS_LDAP_ERROR:
		case STATUS_TEXT_ERROR:
		case STATUS_COM_ERROR:
		case STATUS_CANCEL_ONE:
		case STATUS_CANCEL_ALL:
		case STATUS_COUNT_CANCEL_HANDLERS:
		case STATUS_VCPP_MOD_NOT_FOUND:
		case STATUS_VCPP_PROC_NOT_FOUND:
		case STATUS_NO_MEMORY:
			return TRUE;
		
		default:
			return FALSE;
	}
}

enum ErrorInfoIndex
{
	EiiCode,
	EiiTitle,
	EiiName,
	EiiOpText,
	EiiCancelHandlerCount,
	EiiCt,
	EiiMax = EXCEPTION_MAXIMUM_PARAMETERS
};

struct CancelOneException{};

void __cdecl SEHTranslatorFunction(unsigned int ExceptionCode, EXCEPTION_POINTERS *ExceptionPointers)
{
	//only one exception has an analogue at this point
	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_CANCEL_ONE)
		throw CancelOneException();
};

struct CppCancelOneHandlerExistance
{
	CppCancelOneHandlerExistance()
	{
		TlsSetValue(CppCancelHandlerCountTls, c_cast<PVOID>(c_cast<ULONG_PTR>(TlsGetValue(CppCancelHandlerCountTls)) + 1));
	}
	~CppCancelOneHandlerExistance()
	{
		TlsSetValue(CppCancelHandlerCountTls, c_cast<PVOID>(c_cast<ULONG_PTR>(TlsGetValue(CppCancelHandlerCountTls)) - 1));
	}
};

ULONG_PTR CountCancelHandlers()
{
	ULONG_PTR Ct = c_cast<ULONG_PTR>(TlsGetValue(CppCancelHandlerCountTls));

	RaiseException(STATUS_COUNT_CANCEL_HANDLERS, 0, 1, &Ct);

	return Ct;
}

void RaiseError(ULONG ExceptionCode, ULONG_PTR ErrorCode, PCWSTR ErrorTitle, PCWSTR ErrorName, BOOLEAN Noncontinuable)
{
	ULONG_PTR Args[EiiCt];
	Args[EiiCode] = ErrorCode;
	Args[EiiTitle] = reinterpret_cast<ULONG_PTR>(ErrorTitle);
	Args[EiiName] = reinterpret_cast<ULONG_PTR>(ErrorName);
	Args[EiiOpText] = reinterpret_cast<ULONG_PTR>(TlsGetValue(OpTextStackTls));
	Args[EiiCancelHandlerCount] = CountCancelHandlers();

	RaiseException(ExceptionCode, Noncontinuable ? EXCEPTION_NONCONTINUABLE : 0, EiiCt, Args);
}

void RaiseCancelOne()
{
	RaiseError(STATUS_CANCEL_ONE, 1, L"Canceled", L"The operation was canceled.", TRUE);
}

void RaiseCancelAll()
{
	RaiseError(STATUS_CANCEL_ALL, 1, L"Canceled", L"All operations were canceled.", TRUE);
}

void RaiseTextError(PCWSTR ErrorName, PCWSTR Operation = NULL, PCWSTR Object = NULL, PCWSTR ErrorTitle = NULL, BOOLEAN Noncontinuable = TRUE)
{
	if(!ErrorTitle)
		ErrorTitle = L"Error";

	if(Operation || Object)
	{
		OpText Ot(Operation, Object);
		RaiseError(STATUS_TEXT_ERROR, 0, ErrorTitle, ErrorName, Noncontinuable);
	}
	else
		RaiseError(STATUS_TEXT_ERROR, 0, ErrorTitle, ErrorName, Noncontinuable);
}

template <class T> T FORCEINLINE RaiseOnTextErrorT(T Result, T ErrorResult, PCWSTR ErrorName, PCWSTR Operation = NULL, PCWSTR Object = NULL, PCWSTR ErrorTitle = NULL, BOOLEAN Noncontinuable = TRUE)
{
	if(Result == ErrorResult)
		RaiseTextError(ErrorName, Operation, Object, ErrorTitle, Noncontinuable);
	return Result;
}

ULONG_PTR CodeFromExceptionRecord(EXCEPTION_RECORD *Er)
{
	switch(Er->ExceptionCode)
	{
	case STATUS_NT_ERROR:
	case STATUS_WIN32_ERROR:
	case STATUS_STDC_ERROR:
	case STATUS_ADSI_ERROR:
	case STATUS_LDAP_ERROR:
	case STATUS_COM_ERROR:
		return Er->ExceptionInformation[EiiCode];
	default:
		return 0;
	}
}

void EndStringAtCrlf(PWSTR String)
{
	String--;
	while(*(++String))
		switch(*String)
		{
		case L'\r':
		case L'\f':
		case L'\n':
			*String = 0;
			return;
		}
}

DWORD Win32ContinueErrors[] =
{
	ERROR_SUCCESS
};

DWORD Win32CancelOneErrors[] =
{
	ERROR_NO_SCROLLBARS
};

void RaiseWin32Error(DWORD Code, BOOLEAN Noncontinuable = TRUE)
{
	for(size_t i = 0; i < RTL_NUMBER_OF(Win32ContinueErrors); i++)
		if(Win32ContinueErrors[i] == Code)
			return;

	for(size_t i = 0; i < RTL_NUMBER_OF(Win32CancelOneErrors); i++)
		if(Win32CancelOneErrors[i] == Code)
			RaiseCancelOne();

	auto_szpod<WCHAR, Win32_LocalHeap_pod_allocator> ErrorName;

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, Code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), c_cast<PWSTR>(ErrorName.assign_indirect()), 0, NULL);

	if(!ErrorName)
		ErrorName = L"Unknown Error";

	EndStringAtCrlf(ErrorName); //Win32 error messages have a nasty habit of ending in newlines

	RaiseError(STATUS_WIN32_ERROR, Code, L"A Win32 Error Has Occured", ErrorName, Noncontinuable);
}

void RaiseNtError(NTSTATUS Status, BOOLEAN Noncontinuable = TRUE)
{
	auto_szpod<WCHAR, Win32_LocalHeap_pod_allocator> ErrorName;

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle(L"ntdll.dll"), Status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), c_cast<PWSTR>(ErrorName.assign_indirect()), 0, NULL);

	if(!ErrorName)
		ErrorName = L"Unknown Error";

	EndStringAtCrlf(ErrorName);

	RaiseError(STATUS_NT_ERROR, Status, L"A System Error Has Occured", ErrorName, Noncontinuable);
}

void RaiseStdCError(errno_t ErrorNum, BOOLEAN Noncontinuable = TRUE)
{
	WCHAR MsgBuff[MAX_STRING_CHARS];

	_wcserror_s(MsgBuff, MAX_STRING_CHARS, ErrorNum);

	EndStringAtCrlf(MsgBuff);

	RaiseError(STATUS_STDC_ERROR, ErrorNum, L"A C Library Error Has Occured", MsgBuff, Noncontinuable);
}

void RaiseComError(HRESULT Result, BOOLEAN Noncontinuable = TRUE)
{
	auto_szpod<WCHAR, Win32_LocalHeap_pod_allocator> ErrorName;

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, Result, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), c_cast<PWSTR>(ErrorName.assign_indirect()), 0, NULL);

	if(!ErrorName)
		ErrorName = L"Unknown Error";

	EndStringAtCrlf(ErrorName); //Win32 error messages have a nasty habit of ending in newlines

	RaiseError(STATUS_WIN32_ERROR, Result, L"A COM Error Has Occured", ErrorName, Noncontinuable);
}

void RaiseInternalError()
{
	RaiseException(static_cast<ULONG>(STATUS_INTERNAL_ERROR), 0, 0, 0);
}

void __cdecl CRT_InvalidParameterHandler(const wchar_t *expression, const wchar_t *function, const wchar_t *file, unsigned int line, uintptr_t)
{
	//ignore error here, allow faulting function to return error status, and eventually possibly call RaiseStdCError (at caller's discretion)
}

DWORD FORCEINLINE RaiseOnWin32Error(DWORD Result, BOOLEAN Noncontinuable = TRUE)
{
	if(Result != ERROR_SUCCESS)
		RaiseWin32Error(Result, Noncontinuable);
	return Result;
}

template <class T> T FORCEINLINE RaiseOnWin32ErrorT(T Result, T ErrorResult = NULL, BOOLEAN Noncontinuable = TRUE)
{
	if(Result == ErrorResult)
		RaiseWin32Error(GetLastError(), Noncontinuable);
	return Result;
}

NTSTATUS FORCEINLINE RaiseOnNtError(NTSTATUS Status, BOOLEAN Noncontinuable = TRUE)
{
	if(NT_ERROR(Status))
		RaiseNtError(Status, Noncontinuable);
	return Status;
}

template <class T> T RaiseOnStdCError(T Result, T ErrorResult = NULL)
{
	if(Result == ErrorResult)
	{
		errno_t err;
		_get_errno(&err);
		RaiseStdCError(err);
	}
	return Result;
}

HRESULT FORCEINLINE RaiseOnComError(HRESULT Result, BOOLEAN Noncontinuable = TRUE)
{
	if(FAILED(Result))
		RaiseComError(Result, Noncontinuable);

	return Result;
}

FORCEINLINE LRESULT RaiseSendMessageListBox(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	return RaiseOnWin32ErrorT<LRESULT>(SendMessage(hWnd, Msg, wParam, lParam), LB_ERR);
}

FORCEINLINE LRESULT RaiseSendMessageComboBox(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	return RaiseOnWin32ErrorT<LRESULT>(SendMessage(hWnd, Msg, wParam, lParam), CB_ERR);
}

FORCEINLINE LRESULT RaiseSendMessageTextBox(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	return RaiseOnWin32ErrorT<LRESULT>(SendMessage(hWnd, Msg, wParam, lParam), FALSE);
}

//note: needs to be bincompatible with OpText
struct StoredOpText
{
	auto_szpod<StoredOpText, default_allocator> Up; //not a pod, don't use default_pod_allocator
	auto_szpod<WCHAR> Operation;
	auto_szpod<WCHAR> Object;
};

template <> size_t auto_szpod_length(StoredOpText const *)
{
	return sizeof(StoredOpText);
}

class StoredException
{
	EXCEPTION_RECORD ExceptionRecord;
	auto_szpod<WCHAR> ErrorTitle;
	auto_szpod<WCHAR> ErrorName;
	auto_szpod<StoredOpText, default_allocator> Text; //self managed linked list
public:
	StoredException()
	{
		RtlZeroStruct(ExceptionRecord);
	}
	StoredException &operator =(EXCEPTION_RECORD const &Src)
	{
		ExceptionRecord = Src;

		if(IsExpectedException(Src.ExceptionCode) && Src.NumberParameters >= EiiOpText)
		{
			ErrorTitle = reinterpret_cast<PCWSTR>(Src.ExceptionInformation[EiiTitle]);

			ErrorName = reinterpret_cast<PCWSTR>(Src.ExceptionInformation[EiiName]);

			StoredOpText *SrcOpText = reinterpret_cast<StoredOpText *>(Src.ExceptionInformation[EiiOpText]);
			if(SrcOpText)
				Text.take(new StoredOpText(*SrcOpText));
			else
				Text.reset();
		}
		
		return *this;
	}
	StoredException &operator =(EXCEPTION_RECORD const *Src)
	{
		return operator =(*Src);
	}
	operator EXCEPTION_RECORD const *()
	{
		//fix up exception information to point here
		ExceptionRecord.ExceptionInformation[EiiTitle] = reinterpret_cast<ULONG_PTR>(ErrorTitle.get());
		ExceptionRecord.ExceptionInformation[EiiName] = reinterpret_cast<ULONG_PTR>(ErrorName.get());
		ExceptionRecord.ExceptionInformation[EiiOpText] = reinterpret_cast<ULONG_PTR>(Text.get());
		return &ExceptionRecord;
	}
	void ReRaiseIf()
	{
		if(ExceptionRecord.ExceptionCode)
			RaiseException(ExceptionRecord.ExceptionCode, ExceptionRecord.ExceptionFlags | EXCEPTION_NONCONTINUABLE, ExceptionRecord.NumberParameters, ExceptionRecord.ExceptionInformation);
	}
};

typedef void (CALLBACK *PUSH_ERROR_TEXT_PROC)(PCWSTR Title, PCWSTR Text, PVOID Context);

BOOLEAN PushLayeredErrorText(EXCEPTION_POINTERS *ExceptionPointers, PUSH_ERROR_TEXT_PROC Dest, PVOID Context)
{
	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_COUNT_CANCEL_HANDLERS)
		return FALSE;

	if(IsExpectedException(ExceptionPointers->ExceptionRecord->ExceptionCode))
	{
		OpText *Link = reinterpret_cast<OpText *>(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiOpText]);

		//base error
		PCWSTR ErrorTitle = c_cast<PCWSTR>(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiTitle]);
		PCWSTR ErrorName = c_cast<PCWSTR>(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiName]);
		Dest(ErrorTitle, ErrorName, Context);

		//append optext links
		while(Link)
		{
			WCHAR Text[MAX_STRING_CHARS];
			Text[0] = 0; //start null-terminated
			
			if(Link->Operation)
				wcsncat_s(Text, Link->Operation, _TRUNCATE);

			if(Link->Object && Link->Operation)
				wcsncat_s(Text, L", ", _TRUNCATE);

			if(Link->Object)
				wcsncat_s(Text, Link->Object, _TRUNCATE);

			Link = Link->Up;

			Dest(NULL, Text, Context); 
		}

		return TRUE;
	}

	return FALSE;
}

BOOLEAN PushErrorText(EXCEPTION_POINTERS *ExceptionPointers, PUSH_ERROR_TEXT_PROC Dest, PVOID Context)
{
	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_COUNT_CANCEL_HANDLERS)
		return FALSE;

	BOOLEAN Expected = IsExpectedException(ExceptionPointers->ExceptionRecord->ExceptionCode);

	if(Expected || HANDLE_ALL_EXCEPTIONS)
	{
		PCWSTR Title = reinterpret_cast<PCWSTR>(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiTitle]);
		WCHAR Text[MAX_STRING_CHARS];
		Text[0] = 0; //start null-terminated
		OpText *Link;

		if(!Expected)
		{
			Title = L"Unexpected Exception";

			swprintf_s(Text, L"%X\n", ExceptionPointers->ExceptionRecord->ExceptionCode);

			//MessageBox(HWND_DESKTOP, Text, Title, MB_OK);

			Link = c_cast<OpText *>(TlsGetValue(OpTextStackTls));
		}
		else
		{
			//append error name
			{
				PCWSTR ErrName = reinterpret_cast<PCWSTR>(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiName]);

				if(ErrName)
					wcsncat_s(Text, ErrName, _TRUNCATE);
			}

			Link = reinterpret_cast<OpText *>(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiOpText]);
		}

		//append optext links
		while(Link)
		{
			wcsncat_s(Text, L"\r\n", _TRUNCATE);
			if(Link->Operation)
				wcsncat_s(Text, Link->Operation, _TRUNCATE);

			if(Link->Object && Link->Operation)
				wcsncat_s(Text, L", ", _TRUNCATE);

			if(Link->Object)
				wcsncat_s(Text, Link->Object, _TRUNCATE);

			Link = Link->Up;
		}

		Dest(Title, Text, Context);

		return TRUE;
	}
	else
		return FALSE;
}

struct PUSH_LOOKUP_ERROR_TEXT_CONTEXT
{
	PUSH_ERROR_TEXT_PROC NextDest;
	PVOID NextContext;
};

ULONG CancelOneBarrierFilter(EXCEPTION_POINTERS *ExceptionPointers)
{
	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_CANCEL_ONE)
		return EXCEPTION_EXECUTE_HANDLER;
	else if(ExceptionPointers->ExceptionRecord->ExceptionCode = STATUS_COUNT_CANCEL_HANDLERS)
		return ExceptionPointers->ExceptionRecord->ExceptionInformation[0]++, EXCEPTION_EXECUTE_HANDLER;
	else
		return EXCEPTION_CONTINUE_SEARCH;
}

void PushLookupErrorTextProc(PCWSTR Title, PCWSTR Text, PVOID Context)
{
	PUSH_LOOKUP_ERROR_TEXT_CONTEXT *LEContext = reinterpret_cast<PUSH_LOOKUP_ERROR_TEXT_CONTEXT *>(Context);

	SIZE_T Text2Len = wcslen(Text) + sizeof(WCHAR) * 3;

	auto_szpod<WCHAR> Text2 = auto_szpod_empty_ct(Text2Len);

	RaiseOnStdCError(swprintf_s(Text2, Text2Len, L"<%s>", Text));

	LEContext->NextDest(Title, Text2, LEContext->NextContext);
}

BOOLEAN PushLookupErrorText(EXCEPTION_POINTERS *ExceptionPointers, PUSH_ERROR_TEXT_PROC Dest, BOOLEAN NameLookup, PVOID Context)
{
	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_COUNT_CANCEL_HANDLERS)
		return FALSE;

	//special cases
	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_WIN32_ERROR || ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_NT_ERROR)
		switch(ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiCode])
		{
		case ERROR_TRUSTED_DOMAIN_FAILURE:
		case ERROR_TRUST_FAILURE:
		case STATUS_TRUSTED_DOMAIN_FAILURE:
		case STATUS_TRUST_FAILURE:
			Dest(L"", L"<Trust failure>", Context);
			return TRUE;

		case ERROR_NONE_MAPPED:
		case STATUS_NONE_MAPPED:
			if(NameLookup)
				Dest(L"", L"<Account name not found>", Context);
			else
				Dest(L"", L"<SID not found>", Context);
			return TRUE;
		}

	//general Win32/stdc error
	PUSH_LOOKUP_ERROR_TEXT_CONTEXT LEContext;

	LEContext.NextContext = Context;
	LEContext.NextDest = Dest;

	return PushErrorText(ExceptionPointers, &PushLookupErrorTextProc, &LEContext);
}

int CALLBACK ShowWin32MessageBoxErrorMessageBox(PCWSTR Title, PCWSTR Text, HWND Parent, BOOLEAN Noncontinuable, BOOLEAN CanCancel)
{
	UINT Type = MB_ICONHAND | MB_TASKMODAL;

	if(Noncontinuable && CanCancel)
		Type |= MB_OKCANCEL;
	else if(!Noncontinuable && CanCancel)
		Type |= MB_ABORTRETRYIGNORE;
	else if(Noncontinuable && !CanCancel)
		Type |= MB_OK;
	else if(!Noncontinuable && !CanCancel)
		Type = MB_RETRYCANCEL;

	return MessageBox(Parent, Text, Title, Type);
}

int (CALLBACK *ShowErrorMessageBoxMessageBox)(PCWSTR Title, PCWSTR Text, HWND Parent, BOOLEAN Noncontinuable, BOOLEAN CanCancel) = &ShowWin32MessageBoxErrorMessageBox;

struct SHOW_ERROR_MESSAGE_BOX_CONTEXT
{
	HWND Parent;
	BOOLEAN Noncontinuable;
	BOOLEAN CanCancel;
	int Result;
};

void ShowErrorMessageBoxProc(PCWSTR Title, PCWSTR Text, PVOID Context)
{
	SHOW_ERROR_MESSAGE_BOX_CONTEXT *MBContext = reinterpret_cast<SHOW_ERROR_MESSAGE_BOX_CONTEXT *>(Context);

	MBContext->Result = ShowErrorMessageBoxMessageBox(Title, Text, MBContext->Parent, MBContext->Noncontinuable, MBContext->CanCancel);
}

int ShowErrorMessageBox(EXCEPTION_POINTERS *ExceptionPointers, HWND Parent)
{
	SHOW_ERROR_MESSAGE_BOX_CONTEXT MBContext;

	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_CANCEL_ONE ||
	   ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_CANCEL_ALL)
		return EXCEPTION_EXECUTE_HANDLER;

	if(ExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_COUNT_CANCEL_HANDLERS)
		return EXCEPTION_CONTINUE_EXECUTION;

	MBContext.Noncontinuable = (ExceptionPointers->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0;
	MBContext.CanCancel = ExceptionPointers->ExceptionRecord->ExceptionInformation[EiiCancelHandlerCount] > 0;
	MBContext.Parent = Parent;
	
	if(!PushErrorText(ExceptionPointers, &ShowErrorMessageBoxProc, &MBContext))
		return EXCEPTION_CONTINUE_SEARCH;

	switch(MBContext.Result)
	{
		case IDABORT:
		case IDCANCEL:
			return EXCEPTION_EXECUTE_HANDLER;

		case IDRETRY:
			return EXCEPTION_CONTINUE_EXECUTION;

		case IDOK:
		case IDIGNORE:
			RaiseCancelOne();
			return EXCEPTION_CONTINUE_EXECUTION;

		default:
			return EXCEPTION_CONTINUE_SEARCH;
	}
}